package arithmetic.functionTest;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.EnumSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.TreeSet;
import java.util.UUID;
import java.util.function.BiConsumer;
import java.util.function.BinaryOperator;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.IntConsumer;
import java.util.function.Supplier;
import java.util.stream.Collector;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.junit.jupiter.api.Test;

import com.google.common.collect.LinkedHashMultimap;
import com.google.common.collect.Multimap;

import lombok.AllArgsConstructor;
import lombok.Data;

/**
 * Created on 2020-04-16
 */
public class TestStream {
    public static void main(String[] args) {
        Stream<String> stream = Stream.of("hello", "helloWorld", "world");

        //Object[] objects = stream.toArray();
        //Object[] objects = stream.toArray(String[]::new);
        Object[] objects = stream.toArray(length -> new String[length]);


        //打印一下
        Arrays.asList(objects).forEach(System.out::println);


        // toList
        List<String> collect = getStream().collect(Collectors.toList());
        System.out.println("collect-------");
        collect.forEach(System.out::println);

        // new 一个list,
        // 将每个元素放入新的list,
        // 将所有list放入最终的list聚合, 这样可以实现并发
        List<String> collect1 = getStream().collect(() -> new LinkedList<String>(), (list, e) -> list.add(e),
                (lists, list) -> lists.addAll(list));
        // 简洁版
        List<String> collect2 = getStream().collect(LinkedList::new, LinkedList::add, LinkedList::addAll);

        // 实现通用一点的方法  构造方法自己传入
        LinkedList<String> collect3 = getStream().collect(Collectors.toCollection(LinkedList::new));
        System.out.println("collect3-------");
        collect3.forEach(System.out::println);
        TreeSet<String> collect4 = getStream().collect(Collectors.toCollection(TreeSet::new));
        System.out.println("collect4-------");
        collect4.forEach(System.out::println);

        // 3.flatMap 将多个集合的集合打平
        Stream<List<Integer>> listStream = Stream.of(Arrays.asList(1), Arrays.asList(2, 4), Arrays.asList(5, 6, 7));
        // 接收一个t,返回流, 将多个流合并为一个流
        listStream.flatMap(list -> list.stream()).map(a -> a * 2).forEach(System.out::println);

        // 4. 无限流
        Stream.generate(() -> UUID.randomUUID().toString()).limit(10).forEach(System.out::println);
        // 方法引用:对象或者类调用
        Stream.generate(UUID.randomUUID()::toString).limit(10).forEach(System.out::println);
        // 迭代器:产生6个奇数 1,3,5,7  忽略两个,取三个,并*2 求和
        Stream.iterate(1, a -> a + 2).limit(6).skip(2).limit(3)
                .peek(System.out::println)
                .map(a -> a * 2)
                // a是 result,b是每个元素
                .reduce((result, ele) -> (result + ele)).ifPresent(System.out::println);

        System.out.println("------");
        // 老师的做法
        int sum = Stream.iterate(1, a -> a + 2).limit(6).skip(2).limit(3)
                .peek(System.out::println)
                .mapToInt(a -> a * 2) //使用这个避免自动装箱拆箱 性能损耗
                // a是 result,b是每个元素
                .sum();
        System.out.println(sum);


        System.out.println("------");
        // 5.流的陷阱
        // 流所有的操作分为中间操作和终止操作,  所有的中间操作没有遇到终止操作时是会惰性执行(lazy)
        Stream.of(1, 2, 3, 4).map(a -> {
            a = a * 2;
            System.out.println(a);
            return a;
        }); //不执行
        System.out.println("====");
        Stream.of(1, 2, 3, 4).map(a -> {
            a = a * 2;
            System.out.println(a);
            return a;
        }).forEach(System.out::println);

        System.out.println("正确写法演示-----");
        // 正确的写法  (一直产生0,1)
        Stream.iterate(1, a -> (a + 1) % 2).limit(6).distinct().forEach(System.out::println);
        System.out.println("问题写法演示-----");
        // (有问题的), 前面无限流一直等待去重,
        Stream.iterate(1, a -> (a + 1) % 2).distinct().limit(6).forEach(System.out::println);

        // 集合和流的对比
        //1. 集合的循环是外部迭代,流是内部迭代.迭代器也是内部迭代   (集合是像命题作文,流像完形填空)
        //2. 集合关注数据与数据存储本身, 流关注数据的计算.
        // 3. 流与迭代器都无法重复使用或消费的

    }

    private static Stream<String> getStream() {
        return Stream.of("hello", "helloWorld", "world");
    }

    @Test
    public void test2() {
        // 流的短路元素, 只有找到符合条件的后面不再运行
        // 找到元素长度为5,打印出来
        getStream().mapToInt(a -> {
            System.out.println(a);
            return a.length();
        }).filter(l -> l == 5).findFirst().ifPresent(System.out::println);
    }

    private static List<String> list;

    private static List<String> getList() {
        if (list == null) {
            list = Stream.generate(UUID.randomUUID()::toString).limit(20000000).collect(Collectors.toList());
        }
        return list;
    }

    /**
     * 测试不成功?? 为啥并行并没有提高多少
     */
    @Test
    public void testParallSteam() {
        getList();

        System.out.println("开始排序");
        long l = System.currentTimeMillis();
        //串行排序:443
        //getList().stream().sorted().count();
        long l1 = System.currentTimeMillis();
        System.out.println("串行排序:" + (l1 - l));

        getList().parallelStream().sorted().count();
        //并行排序:492
        long l2 = System.currentTimeMillis();
        System.out.println("并行排序:" + (l2 - l1));


    }


    @Test
    public void test3() {
        List<String> list = Arrays.asList("hello world", "hello welcome", "welcome world", "hello world hehe");
        // 要求: 去重输出单个
        list.stream().flatMap(a -> Stream.of(a.split(" "))).distinct().forEach(System.out::println);

    }

    /**
     * 分组
     * 类似sql语句中的分组
     * 和 分区
     * groupBy
     */
    @Test
    public void test4() {
        // 要求: 去重输出单个
        List<Student> students = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            students.add(new Student("zhangsan" + i, 88 + i));
        }
        students.add(new Student("zhangsan" + 1, 88 + 2));

        Map<String, List<Student>> collect = students.stream().collect(Collectors.groupingBy(Student::getName));
        System.out.println(collect);

        // 分组后返回个数
        System.out.println("----");
        Map<String, Long> collect1 =
                students.stream().collect(Collectors.groupingBy(Student::getName, Collectors.counting()));
        System.out.println(collect1);

        // 分组后 找到分数最大的
        System.out.println("----");
        Map<String, Optional<Student>> collect2 = students.stream().collect(
                Collectors.groupingBy(Student::getName, Collectors.maxBy(Comparator.comparing(Student::getScore))));
        System.out.println(collect2);
        Map<String, Student> collect4 = students.stream()
                .collect(
                        Collectors.groupingBy(Student::getName, Collectors
                                .collectingAndThen(Collectors.maxBy(Comparator.comparing(Student::getScore)),
                                        Optional::get)));

        Map<String, Student> collect5 = students.stream().collect(
                Collectors.toMap(Student::getName, Function.identity(),
                        BinaryOperator.maxBy(Comparator.comparing(Student::getScore))));


        System.out.println("----");
        // 分组后求分数平均值
        Map<String, Double> collect3 = students.stream()
                .collect(Collectors.groupingBy(Student::getName, Collectors.averagingDouble(Student::getScore)));
        System.out.println(collect3);
    }

    class Student {
        String name;
        int score;

        public String getName() {
            return name;
        }

        public int getScore() {
            return score;
        }

        public void setName(String name) {
            this.name = name;
        }

        public void setScore(int score) {
            this.score = score;
        }

        public Student() {
        }

        public Student(String name, int score) {
            this.name = name;
            this.score = score;
        }
    }


    /**
     * 比较器,类型推断
     */
    @Test
    public void test5() {
        List<String> list =
                Arrays.asList("hello", "world", "hello world", "hello welcome", "hello world hehe", "welcome world");
        //list.sort((a, b) -> a.length() - b.length());// 升序
        //list.sort(Comparator.comparingInt(String::length));// 升序
        //list.sort((a, b) -> b.length() - a.length());// 降序

        //Collections.sort(list, Comparator.comparingInt(String::length));
        //Collections.sort(list, Comparator.comparingInt(String::length).reversed());
        //Collections.sort(list, Comparator.comparingInt(a->a.length()).reversed()); //无法推断
        //Collections.sort(list, Comparator.comparingInt((String a)->a.length()).reversed()); //加入变量类型后才可编译,推断

        System.out.println(list);

        // 二级排序
        Collections.sort(list, Comparator.comparingInt(String::length)
                .thenComparing(String::toLowerCase, Comparator.reverseOrder())); //加入变量类型后才可编译,推断
        System.out.println(list);


    }

    /**
     * 自定义收集器
     */
    @Test
    public void test6() {
        List<String> list =
                Arrays.asList("hello", "world", "hello world", "hello welcome", "hello world hehe", "welcome world");
        List<String> collect = list.stream().collect(new Collector<String, List<String>, List<String>>() {
            // 创建容器
            @Override
            public Supplier<List<String>> supplier() {
                System.out.println("supplier invoke");
                return ArrayList::new;
            }

            // 做每个的中间步骤,结束两个参数 不返还结果
            // 第一个是中间类 (就是List<String>)
            // 第二个是元素 就是String
            @Override
            public BiConsumer<List<String>, String> accumulator() {
                System.out.println("accumulator invoke");
                return (list, e) -> list.add(e);
            }

            @Override
            public BinaryOperator<List<String>> combiner() {
                System.out.println("combiner invoke");
                return (lists, list) -> {
                    lists.addAll(list);
                    return lists;
                };
            }

            // 最后的强制类型转换
            @Override
            public Function<List<String>, List<String>> finisher() {
                return i -> i;
            }

            // 定义收集器的特性: 是否可无序,是否可并发,是否可省略地四步直接强制类型转换. 可以一个或多个特性,也可以为空
            @Override
            public Set<Characteristics> characteristics() {
                System.out.println("characteristics invoke");
                // IDENTITY_FINISH 不需要调用finisher() 的意思,中间结果即最终结果,直接用类型强转即可
                // CONCURRENT 即可用被并发执行
                // UNORDERED 无序
                return Collections.unmodifiableSet(EnumSet.of(Collector.Characteristics.IDENTITY_FINISH));
            }
        });

        System.out.println(collect);


    }

    /**
     * 输入list,输出map,key为首字母
     * 自定义收集器
     */
    @Test
    public void test7() {
        List<String> list =
                Arrays.asList("hello", "world", "hello world", "hello welcome", "hello world hehe", "welcome world");
        Map<String, Collection<String>> collect =
                list.stream().collect(new MyCollectors());
        System.out.println(collect);

        System.out.println("------并行流");
        Map<String, Collection<String>> parallelStreamCollect =
                list.parallelStream().collect(new MyCollectors());
        System.out.println(parallelStreamCollect);
    }


    static class MyCollectors implements Collector<String, Multimap<String, String>, Map<String, Collection<String>>> {

        // 创建容器
        @Override
        public Supplier<Multimap<String, String>> supplier() {
            return LinkedHashMultimap::create;
        }

        // 做每个的中间步骤,结束两个参数 不返还结果
        @Override
        public BiConsumer<Multimap<String, String>, String> accumulator() {
            return (multimap, e) -> {
                System.out.println("multimap" + multimap + " e:" + e + " " + Thread.currentThread().getName());
                multimap.put(e.substring(0, 1), e);
            };
        }

        // 合并
        @Override
        public BinaryOperator<Multimap<String, String>> combiner() {
            System.out.println("combiner invoke");
            return (multimaps, multimap) -> {
                multimaps.putAll(multimap);
                return multimaps;
            };
        }

        // 最后的强制类型转换
        @Override
        public Function<Multimap<String, String>, Map<String, Collection<String>>> finisher() {
            return Multimap::asMap;
        }

        @Override
        public Set<Characteristics> characteristics() {
            System.out.println("characteristics invoke");
            // 加个CONCURRENT后 中间的结果只有一个, 不需要合并了 combiner未被执行
            return Collections.unmodifiableSet(EnumSet.of(Characteristics.UNORDERED, Characteristics.CONCURRENT));
        }
    }

    /**
     * 函数编程
     * 测试方法引用时的编译 intCusumer
     */
    @Test
    public void test8() {
        IntConsumer intCusumer = i -> System.out.println(i);
        Consumer cusumer = i -> System.out.println(i);
        //lamda 表达式一样可以赋给不同的Consumer, intConsumer和Cosumer没有类上继承关系,但是Consumer的泛型包括了intConsumer


        System.out.println(intCusumer instanceof Consumer);//false
        System.out.println(intCusumer instanceof IntConsumer);//true

        // 1 可以正常调用
        testFunction(i -> System.out.println(i));
        testFunction(intCusumer);
        // 2 不能调用
        //testFunction(cusumer);

        // 强转 (把cusumer)
        testFunction(cusumer::accept); // 函数式的方式,相当于传递了lamda表达式过去了
        // 错误的强转
        testFunction((IntConsumer) cusumer); //面向对象的方式

    }

    public void testFunction(IntConsumer intCusumer) {
        intCusumer.accept(1);
    }

    /**
     * 测试发现stream的内部逻辑
     */
    @Test
    public void test9() {
        List<String> list =
                Arrays.asList("hello", "world", "qwr");

        // 1. 先进入ReferencePipeline.HEAD里的 forEach方法
        // 注意每个list 都会自己实现分割迭代器里的 forEachRemaining方法,
        //list.stream().forEach(System.out::println);

        //list.stream().map(a -> a).forEach(System.out::println);
        Stream<String> stream = list.stream();
        System.out.println("1-----");
        Stream<String> stream1 = stream.map(a -> a);
        System.out.println("2-----");
        Stream<String> stream2 = stream1.filter(a -> a.length() > 3);
        stream2.forEach(System.out::println);
    }


    /**
     * this
     * 匿名内部类和  lamda表达式的区别
     */
    @Test
    public void test10() {
        Runnable r1 = () -> System.out.println(this + "r1");
        Runnable r2 = () -> System.out.println(this + "r2");
        Runnable r3 = new Runnable() {
            @Override
            public void run() {
                System.out.println(this + "r3");
            }
        };
        Runnable r4 = new Runnable() {
            @Override
            public void run() {
                System.out.println(this + "r4");
            }
        };

        new Thread(r1).start();
        // arithmetic.functionTest.TestStream$2@16bbc510  匿名内部类
        new Thread(r2).start();
        new Thread(r3).start();
        new Thread(r4).start();
    }


}
